﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Threading;
using System.Net;
using System.Collections;
using GiMessCommons;

namespace GiMessServer.Net
{    
    public delegate void NewNetMsgEventHandler(NetClient sender, NetMsg msg);

    public class NetServer
    {
        private TcpListener  serverListener;
        private Thread       acceptThread;
        private Thread       workThread;
        private Thread       handleThread; //TODO thread ptr handler NEIMPLEMENTAT //UPDATE e implementat prin ThreadPool
        private NetMsg       recvMsg;

        private volatile ArrayList clientsList;

        private int port = NetConsants.ConnectionPort;

        public event NewNetMsgEventHandler NewNetMsg;

        public NetServer()
        {
            IPAddress ipAddress = IPAddress.Parse(NetConsants.ConnectionIP);
            IPEndPoint ipEndPoint = new IPEndPoint(ipAddress, port);

            serverListener = new TcpListener(ipEndPoint);
            serverListener.Start();

            clientsList = new ArrayList();

            acceptThread = new Thread(new ThreadStart(ListenForClients));
            acceptThread.Name = "serverAcceptThread";
            acceptThread.Start();

            workThread = new Thread(new ThreadStart(Listen));
            workThread.Name = "serverWorkThread";
            workThread.Start();
        }

        public void HeartBeat()
        {
            foreach (NetClient netClient in clientsList)
            {
                netClient.Receive();

                if (!netClient.IsBufferEmpty())
                {
                    recvMsg = netClient.NextMessage();
                    while (recvMsg != null)
                    {
                        // apelez eventu
                        //NewNetMsg(netClient, recvMsg);
                        NetClient netClientCopy = netClient;
                        NetMsg    recvMsgCopy   = recvMsg;
                        
                        ThreadPool.QueueUserWorkItem(s => NewNetMsg(netClientCopy, recvMsgCopy));
                        recvMsg = netClient.NextMessage();
                    }
                }
            }
            for (int i = 0; i < clientsList.Count; i++)
            {
                NetClient netClient = clientsList[i] as NetClient;
                if(!netClient.IsConnected())//if (!netClient.Client.Connected)
                {
                    DisconnectClient(netClient); //TODO determina daca un client e conectat //TODO baga asta in for separat
                }
            }
        }

        public void Listen()
        {
            System.Timers.Timer timer = new System.Timers.Timer(); //TODO de scos chestiile astea de debug(revenire la r19)
            int heartbeats = 0;
            timer.Interval = 10000;
            timer.AutoReset = true;
            timer.Elapsed += (s,p) => { Loger.LogLine((heartbeats/10).ToString(), Loger.ServerLogLevel.Verbose); heartbeats = 0; };
            timer.Start();
            while (true)
            {
                HeartBeat();
                heartbeats++;
                Thread.Sleep(1);
            }
        }

        /// <summary>
        /// Trimite un mesaj la un client
        /// </summary>
        /// <param name="netClient">Clientul caruiia ii trimite mesajul</param>
        /// <param name="msg">Mesajul trimis</param>
        /// <returns></returns>
        public bool SendNetMsg(NetClient netClient, NetMsg msg)
        {
            return netClient.SendNetMsg(msg);
        }

        public bool SendNetMsg(NetClient netClient, NetServices service, string data)
        {
            NetMsg msgToSend = new NetMsg(service, data);
            return SendNetMsg(netClient, msgToSend);
        }

        public bool SendNetMsg(string userName, NetMsg msg)
        {
            foreach (NetClient client in clientsList)
            {
                if (client.Credidentials.UserName == userName)
                {
                    return client.SendNetMsg(msg);
                }
            }
            return false;
        }

        public bool SendNetMsg(string userName, NetServices service, string data)
        {
            NetMsg msgToSend = new NetMsg(service, data);
            return SendNetMsg(userName, msgToSend);
        }

        public void SendNetMsgToAll(NetMsg msg)
        {
            foreach (NetClient netClient in clientsList)
            {
                //SendNetMsg(netClient, msg);
                netClient.SendNetMsg(msg);
            }
        }

        /// <summary>
        /// Sterge un client din lista de clienti si ii inchide socketu.
        /// </summary>
        /// <param name="netClient"></param>
        public void DisconnectClient(NetClient netClient)
        {
            Loger.LogLine("Disconnecting client", Loger.ServerLogLevel.Verbose);
            netClient.Client.Close();
            clientsList.Remove(netClient);
        }

        private void ListenForClients()
        {
            while (true)
            {
                NetClient client = new NetClient();
                client.Client    = serverListener.AcceptTcpClient();
                client.Init();

                Loger.LogLine("Client Connected", Loger.ServerLogLevel.Verbose);

                //in caz ca se trimit la toti clientii odata sau altceva si se conecteaza un alt client
                lock (clientsList)
                {
                    clientsList.Add(client);
                }
            }
        }
    }
}
